앰비언트 모드
개요
이스티오 앰비언트 모드는 2022년 처음 선보여진 최신 모드로[1], 2024년에 이스티오 1.24버전이 나오면서 GA가 됐다.[2]
각 워크로드 별로 프록시를 배치하던 방식에서 벗어나 노드 별로 프록시를 배치한다.
또한 프록시 기능을 L4(Ztunnel), L7(Waypoint)으로 분리하여 L4에서만 이뤄지는 기능을 훨씬 빠르게 동작하도록 지원한다.
L7 관련 기능이 필요하다면 그때만 L7을 이용하도록 세팅해주면 된다.
기존 사이드카 모드와 다르게 워크로드마다 프록시를 배치하지 않는다고 하여 Sidecar-less 모드라고도 부른다.
ambient는 "주변의, 은은하게 존재하는"이란 의미를 가지고 있다.
워크로드에 딱 달라붙어 메시로서의 기능을 제공하던 사이드카 방식과 달리 워크로드에 바로 붙지 않지만 주변에 배치되어 자연스럽게 각종 메시의 기능을 제공한다는 점에서 이러한 명칭이 붙은 것으로 보인다.
자원 소모량이나 속도에서 월등하나, 나온지 오래 되지 않아서 아직 기능이 제한적인 구석이 더러 있다.
현재 이스티오를 정리한 내 노트들은 전부 사이드카를 기반으로 설명하고 있기에, 이 문서에서는 앰비언트 모드의 기술적 특징을 정리하는데에만 그치지 않고 나온 배경부터 시작해 가급적 모든 원리들을 담고자 한다.
(물론 실습에 기반한 분석 내용은 토픽으로 뺄 것이다.)
그러나 앰비언트가 버전 업하며 이뤄진 변경 사항을 전부 세세하게 다루진 않을 것이다.
배경
사실 사이드카 모드의 이스티오만으로 서비스 메시로서의 기능들은 이미 충분히 제공되고 있다.
워크로드들은 네트워크 레이어에서 무슨 일이 발생하는지도 모르는 채로 정상적으로 통신이 가능하며, 트래픽 제어를 받을 수 있게 된다.
그럼 어떤 수요가 있어서, 기존 모드에 어떤 아쉬움이 있어서 새로운 모드가 고안됐을까?
기존 사이드카 모드 방식에서의 문제점들을 정리해보자.
사실 이스티오에서 처음 앰비언트에 대한 논의가 나왔던 시점을 찾아서 이런 아키텍처가 나온 배경과 발전 과정을 참고하고 싶었는데, 22년에 처음 발표된 자료 이전의 내용은 빠르게 찾기 힘들 것 같았다.
그래서 여태 나온 글들을 바탕으로 내 생각을 얹어 문제 정의 및 접근 방식을 정리했다.
워크로드와의 높은 결합도
쿠버네티스에서 사이드카 방식은 파드 안에 승인 제어를 통해 사이드카 프록시를 추가 컨테이너로 주입시키는 방식으로 이뤄진다.
그리고 여기에 엔보이가 구동되며 이 놈이 워크로드로 통하는 모든 트래픽(사실 tcp 한정)을 담당하게 된다.
이로부터 발생하는 몇 가지 아쉬운 점이 있다.[3]
- 침입성(Invasiveness)
- 워크로드 파드에 같이 주입되기 때문에, 사이드카는 워크로드와 라이프사이클을 같이 하게 된다.
- 서비스 메시 운영 작업이 워크로드의 동작에 직접적으로 간섭을 하게 된다는 말과 같다.
- 이스티오를 처음 도입하려면 파드에 사이드카가 주입되도록 재배포하는 작업이 필요하다.
- 사이드카의 버전을 올리려면 파드를 아예 새로 띄워야만 한다.
- 리소스 활용도 저하
- 파드를 하나의 단위로 봤을 때 사이드카가 워크로드의 자원을 공유하는 구조가 돼버린다.
- 그래서 워크로드에 영향을 주지 않도록 사이드카를 위해 CPU, 메모리 등의 자원을 넉넉하게 프로비저닝해야 한다.
- 이는 당연히 비용을 최적화하는데 방해된다.
불필요한 리소스 사용
엔보이가 아무리 고성능이라고는 하나, 워크로드의 다양한 트래픽을 처리하는 단일 지점으로서 동작하면서 발생하는 자원 낭비가 있다.
L7 계층에서의 트래픽을 처리에 있어 엔보이는 매우 강력한 솔루션이다.
그러나 단순 TCP 연결 처리, L4 계층에서의 트래픽 제어만 필요로 하는 상황에서도 가장 성능 좋은 선택지라고 보기는 어렵다.
워크로드마다 긴밀하게 결합된 엔보이는 여러 필터를 거치며 궁극적으로 HCM 필터(http 관련 필터) 내부 라우터 필터까지 도달해야 비로소 업스트림 클러스터로 향할 수 있게 된다.
L4에서 처리될 수 있는 기능들은 생각보다 많다.
- TCP 라우팅
- TCP 기반 커넥션 풀 제어(유휴 시간 세팅, 풀 크기 조절 등)
- TLS 암호화
- 모든 서비스 간의 통신은 기본적으로 암호화되는데, 이것은 메시를 제로 트러스트 환경으로 구성하는 핵심 기능이다.
- 인증서 기반 신원 인증 및 인가 정책
이 정도의 기능만으로도 이스티오는 충분히 매력적인 솔루션이며, 이러한 사용 케이스들이 꽤나 많았기 때문에, 이스티오 커뮤니티는 L4 레벨에서의 성능 최적화를 코어한 목표 중 하나로 설정했다.[4]
출발지 기반 트래픽 제어의 비효율성
이와 더불어 이스티오에서 자체적으로 취하는 접근 방식 자체에서 발생하는 문제도 존재했다.
간단하게 요약하면 출발지 기반 트래픽 제어 방식의 비효율이 발생하는 것이다.
이스티오에서 A,B,C 서비스가 존재하는 상황을 생각해보자.
이때 C 서비스로 가는 트래픽 규칙들을 설정하는 이스티오 버츄얼서비스 리소스를 만들면, 이 설정은 A, B에 반영된다.
A에서 C로 출발하는 트래픽은 엔보이의 아웃바운드 핸들러(15001포트)가 수신하고, 내부에 C에 대한 설정을 가지고 있는 가상 리스너에서 각종 처리 과정을 거치는 식으로 이스티오가 설계됐기 때문이다.
이러한 로직은 재시도, 클라이언트 기반 로드밸런싱, 헤더 설정 등 아주 다양한 트래픽 설정을 가능하게 만든다는 장점이 있다.
그러나 동시에 여러 서비스가 복잡하게 얽힌 메시 속에서 각 엔보이는 모든 서비스의 설정 정보를 각각 받야아 한다!
- 컨트롤 플레인은 한 서비스에 대한 설정을 반영하고자 모든 엔보이에 설정을 전파해야 한다.
- 각 엔보이는 모든 서비스에 대한 설정 정보를 가지고 있어 뭐 하나 변경될 때마다 설정을 받아야 한다.
그래서 이걸 최적화하려면 리소스에 exportTo
필드를 세팅하고, 이스티오 사이드카 리소스로 적용 범위를 제한하고 등의 귀찮고 복잡한 작업이 필수적으로 요구된다.
더불어 이 방식은 직관적이지 않아 높은 러닝커브를 야기한다는 것도 문제이다.
공식 블로그에서는 구체적으로 다음의 지점에서 문제를 정리한다.[5]
- 스케일링
- 출발지 사이드카는 메시 내 모든 목적지의 정보를 알아야 한다.
- 그래서 설정 정보의 양이 다항적으로(polynomial, 선형보다 가파른) 증가한다.
- 목적지 설정이 변경되면 모든 사이드카에 해당 정보를 반영해야 한다.
- 디버깅
- 정책 설정이 클라이언트와 서버 사이드카에 분리 적용되어 트러블슈팅이 어렵다.
- 이스티오 데스티네이션룰 관련 설정은 양쪽에 적용돼서 구체적인 디버깅이 정말 어렵다고 생각한다.
- 혼합 환경
- 메시와 비-메시 환경의 클라이언트에 대해 일관적인 동작을 기대할 수 없다.
- 소유권과 귀속(ownership and attribution)
- 한 네임스페이스에 적용하고자 하는 정책은 그 네임스페이스에만 한정되는 것이 바람직하나, 현재는 모든 사이드카에 영향을 미친다.
- 보안적으로 안전한 디자인일지라도, 최적의 방법은 아니다.
새로운 접근 방식 - 앰비언트 모드
이런 문제들을 해결하고자 이스티오에서 제시한 해결책이 바로 앰비언트 모드이다.
가장 기본적인 접근은 네트워킹 기능을 제공하는 프록시를 워크로드로부터 분리해내는 것이다.
한 물리 자원인 노드를 여러 컨테이너가 나눠 사용하듯이, 네트워크 기능 역시 멀티 테넌시 아키텍처로 구성하는 접근하는 것.
그러면서도 이스티오가 서비스 메시로서 가지는 기능들을 유지하기 위해, 가장 처음에는 엔보이를 공용으로 두는 방향으로 설계됐을 것으로 보인다.
L4와 L7 프록시의 분리
엔보이를 공용으로 사용하는 것은 여러 문제를 가지고 있다.[6]
- 엔보이는 멀티 테넌시를 고려한 소프트웨어가 아니다.
- 여러 워크로드의 L7 설정들을 받아내기 위해서는 지나치게 복잡한 규칙 설정해야 하고 보안 위협을 야기한다.
- L7의 작업은 큰 오버헤드를 가진다.
- 이스티오 환경에서 가장 큰 레이턴시를 유발하는 부분은 사실 L7작업이다.
- 최고 추상화 수준에서 트래픽을 제어하는 정교한 필터들이 적용되고 있기 때문이다.
- L4 레벨에서의 트래픽을 처리하기 위해 단일의 엔보이를 사용하는 것 자체가 이상적이지 않다.
이스티오에서는 효율적인 자원 공유를 위한 새로운 아이디어를 제시하는데, 바로 L4 계층과 L7 계층의 프록시 동작을 분리해내는 것.
L7 프록시로서의 엔보이와 별개로, L4 단계에서 요구되는 기능들을 수행할 수 있는 별도의 프록시를 도입하는 것이다.
위 그림에서 보다시피, 비단 L7까지 가지 않고도 L4에서 할 수 있는 작업이 여럿 있다.
TCP 영역의 라우팅, 메트릭, 로깅이야 사실 당연한 거고, 가장 핵심은 제로 트러스트가 아닐까 한다.
이스티오의 핵심 기능 중 하나는 Zero Trust 환경을 구성할 수 있다는 것으로, 모든 서비스가 서로를 신뢰하지 않으며 통신 간 상호 인증을 요구할 수 있다.
이는 SPIFFE 양식 기반의 신원을 상호 요구하며 인증될 경우에만 mTLS를 통한 암호화 통신을 하는 것으로 충족된다.
결과적으로 이스티오 앰비언트 모드는 다음의 두 가지 프록시를 구성한다.
- Ztunnel - Zero trust tunnel, 즉 L4에서 상호 트래픽 간 암호화나 라우팅을 수행하는 프록시
- 엄격한 암호화와 빠른 속도를 지원하며 모든 노드에 배포되어 노드의 워크로드의 트래픽을 관리한다.
- Waypoint - L7에서 기존의 이스티오의 다양한 기능들을 수행하는 엔보이 프록시
- 트래픽 분할, 재시도, 비율 분할, 헤더 기반 인가 정책 등 L7의 막강한 기능을 지원한다.
- 쿠버네티스 네임스페이스 단위로 배포되어 클러스터 네이티브한 관리가 가능하다.
각각에 대해서는 아래에서 조금 더 자세히 다룬다.
목적지 기반 트래픽 제어
사이드카 모드와 비교해서 괄목할 만한 변경점 중 하나는 엔보이 설정 방식에 있다고 생각한다.
계층을 분리하고도 L7의 기능을 수행하기 위해 여전히 엔보이를 사용하는데, 이때 엔보이 사용 방식을 출발지 기반에서 목적지 기반으로 바꾼 것이다.[7]
즉, 이스티오의 L7 기능들은 보내는 측에서 적용되는 것이 아니라 목적지의 엔보이인 웨이포인트가 담당하게 된다는 것이다.
이러한 설정 방식의 변화는 위에서 말한 단점들을 완벽히 없애준다.
이 표는 간단한 예시를 든 것이다.
만약 두 네임스페이스에 10개의 파드를 가진 디플로이먼트가 25개 있다면, 설정의 횟수가 사이드카 모드에서는 12500번에 달한다.
반면 웨이포인트의 방식에서는 고가용성을 위해 웨이포인트를 두 개씩 설정하더라도 설정의 개수는 100개로 압도적으로 설정 양이 줄어들게 된다!
이러한 방식에서는 기존 사이드카 모드에서 최적화를 위해 요구됐던 각종 기법을 사용할 필요가 전혀 없다.
또한 설정하고자 하는 대상에 그대로 설정이 적용된다는 직관성이 부여된다.
물론 사이드카 모드에서 출발지 기반으로 트래픽을 제어하는 방식을 택한 것에는 다 이유가 있다.
HTTP 요청 타임아웃, 재시도 등의 기능은 사실 출발지 쪽에서 설정하는 게 당연하게 느껴지기도 한다.
이것에 대해서는 어떻게 해결하는지 보자.
아키텍처
전체적인 아키텍처를 그리는 게, 한 그림으로 표현하기는 꽤 어려운 것 같다.
각 요소들을 간단하게 이야기해보자면..
- ztunnel
- 데몬셋으로 각 노드에 배치되며 노드 내의 모든 워크로드의 통신을 책임진다.
- waypoint
- L7 기능이 필요할 때 사용자가 선택적으로 배치할 수 있고, 네임스페이스 종속적으로 노드와는 관련이 없다.
- istio-cni
- ztunnel의 암호 통신을 구성을 돕기 위헤 데몬셋으로 배치되어 작업을 수행한다.
istiod는 그냥 이전과 똑같은 istiod이다.
노드와 관련이 있진 않기에 그냥 편의상 바깥으로 빼버렸다.
기존 사이드카 모드와 핵심적으로 비교해서 볼 지점은 바로 이스티오의 모든 컴포넌트가 개별 워크로드와 분리됐다는 것이다.
즉 앰비언트 모드는 서비스에 대해 완벽하게 멀티 테넌시 환경으로 동작한다.
중간에 갈색 선은 암호화 통신 터널을 나타내는데 이 통신은 HBONE이라는 프로토콜을 기반으로 이뤄진다.
이것은 이스티오에서 자체적으로 제작한 15008포트를 사용하는 암호화 터널링 프로토콜인데, 동작 원리에서 조금 더 자세히 다룬다.
본격적으로 동작 구조를 자세히 살펴보기 이전에, 앰비언트의 핵심적인 컴포넌트인 ztunnel과 waypoint의 기능적 측면을 조금 더 알아보자.
Ztunnel
Zero-trust tunnel, 줄여서 Ztunnel은 데몬셋으로 배치되어 각 노드 내의 워크로드의 신원을 제공하고 통신 간 암호화를 수행한다.
신원과 암호화에 초점이 맞추어져 있다보니 이름이 제로 트러스트 터널인 것도 은근 자연스러운 것 같다.
Ztunnel은 워크로드 입장에서는 완벽하게 보이지 않는 상태로 구축되며, 워크로드 라이프사이클과는 무관하기에 정말 '앰비언트'한 프록시로서 기능한다고 할 수 있겠다.
기능
구체적으로 ztunnel이 제공하는 기능은 다음과 같다.
- 워크로드 트래픽 암호화
- 워크로드간의 통신은 전부 mtls 기반으로 암호화되며, HBONE 터널을 이용한다.
- 기존 트래픽에 대한 일체의 변형 없이 전체를 감싸 해당 터널을 통해 송수신한다.
- 암호화 시에 각 워크로드의 고유한 신원을 활용한다.
- 워크로드로 들어가게 될 트래픽에 대한 복호화도 당연히 수행한다.
- 워크로드간의 통신은 전부 mtls 기반으로 암호화되며, HBONE 터널을 이용한다.
- 노드 내 워크로드의 신원 관리
- 노드 내의 워크로드 신원 인증서는 ztunnel이 전부 가지고 관리한다.
- 기본적으로 ztunnel은 다른 컴포넌트와의 통신 없이 알아서 노드 내의 워크로드들을 인식하고 있는 상태.
- istiod로부터 어떤 워크로드가 메시로 관리된다는 설정을 받게 되면 해당 워크로드의 인증서를 CA서버(보통 istiod)에 요청한다.
- 그래서 워크로드는 자신의 신원에 대한 일체의 정보에 접근할 수 없는 상태로 메시에서 신원을 가질 수 있다.
- 노드 내의 워크로드 신원 인증서는 ztunnel이 전부 가지고 관리한다.
- TCP 레벨 라우팅과 접근 제어, 메트릭 제공
- L4 레벨에서 할 수 있는 작업들을 수행한다!
신원과 암호화, 그리고 접근 제어는 사이드카 모드 때와 동일하게 스피페 양식의 워크로드 별 x509 인증서를 기반으로 한다.
ztunnel은 고급 확장 기능 없이 단순하게? 이 기능들을 충족하기만 하면 되는 프록시였다.
여기에 L7 기능을 포함하고 있는 엔보이는 지나치게 과분한 솔루션이며, 또한 터널링 관련 설정이 복잡하다는 한계가 있었다.
(초기 앰비언트 모드가 발표될 당시의 Ztunnel은 엔보이를 이용해 구현됐는데,[8] 터널링 관련 설정에 필터가 4개나 요구됐다고 한다.)
그리 하여 이스티오는 러스트를 기반으로 새로운 맞춤형 솔루션을 자체 개발하기에 이르렀다.
번외 - 왜 러스트인가?
ztunnel은 안전하고, 빠른 경량 프록시란 비기능적 요구사항에 더불어 위의 기능적 요구사항을 가지고 있었다.
이스티오에서는 이러한 특수한 사용 사례에 집중하여 개발된 자체 솔루션이 가장 효율적이라는 가정 아래 개발에 착수했다.
초기에는 친숙한 고 언어를 통해 구현하려 했으나, 둘 다 간단하게 만들어 테스트해본 결과 고는 구렸다고 한다.[9]
또한 충분히 성숙한 Tokio(동시성 관련), Hyper(네트워크 관련) 라이브러리를 기반으로 원하는 기능들이 구현될 수 있다는 것을 확인한 이후 ztunnel이 개발됐다.
벤치마크 내용을 보면 러스트의 압도적인 메모리 사용량이 눈에 띄긴 한다.[10]
구조
위 기능들을 충족하기 위한 ztunnel의 구조는 꽤 간단한 편으로 두 가지의 비동기 런타임이 돌아가고 있다.[11]
- 메인 스레드 - ztunnel의 전체 관리용 스레드
- 워커 스레드 - 워크로드 간 통신을 수립하고 연결하는 작업을 하는 스레드
여기에서 메인 스레드의 구조를 구체화시키자면..
L4 제어와 메트릭을 제공하는 컴포넌트가 있고, istiod로부터 통신을 하는 두 가지 클라이언트를 가지고 있다.
프로세스 실행 시 자신의 서비스 어카운트 토큰을 이용해 컨트롤 플레인에 보안 연결을 수립한 이후, XDS 설정을 받는다.
XDS라고 부르지만, 엔보이의 양식과는 매우 다른데 아무튼 이 설정 정보를 바탕으로 워크로드의 트래픽을 가로채거나, 제어 정책에 대한 설정을 진행한다.
그리고 노드 내 워크로드의 인증서를 관리하는 클라이언트는 필요할 때마다 인증서를 갱신하고 생성 요청하는 작업을 수행한다.
istiod는 해당 요청을 한 주체가 ztunnel이 맞는지를 검사하고, 맞을 경우에만 해당 요청을 수행해준다.
주목할 지점은 워크로드의 신원 인증서가 더 이상 워크로드에 속하지 않고 담당 ztunnel이 관리한다는 것!
(인증서 갱신, 생성 요청은 큐를 통해 관리되는데, 새로운 워크로드의 인증서 생성을 항상 우선하게 설정한다고 한다.)
워커 스레드가 하게 되는 일에 대해서는 동작 원리에서 조금 더 다루게 될 것이다.
설정
ztunnel이 개발될 때 고려된 핵심적인 아이디어 중 하나는 설정 양식의 간소화였다.
이름은 엔보이와 똑같이 그대로 XDS이지만 기존 양식과 매우 큰 차이가 있다.
기존 엔보이의 설정 양식으로 치면 하나의 파드에 대해나 설정을 전달하기 위해서 yaml 파일 기준으로 무려 350줄 가량의 데이터를 전달해야만 했다.
엔보이 기반 ztunnel을 사용해봤을 때 설정 양이 다항적으로 커졌다고 한다.
그래서 ztunnel을 개발할 때 명확하게 필요한 정보만을 효율적으로 담는 양식을 구체화시켰다.
name: helloworld-v1-55446d46d8-ntdbk
namespace: default
serviceAccount: helloworld
node: ambient-worker2
protocol: TCP
status: Healthy
waypointAddresses: []
workloadIp: 10.244.2.8
canonicalName: helloworld
canonicalRevision: v1
workloadName: helloworld-v1
workloadType: deployment
개별 파드를 식별하여 메시 내의 하나의 단위로 나타내기 위해 필요한 정보는 사실 이 정도가 전부이다.
ztunnel은 간소화시킨 명세를 바탕으로 istiod와 통신하는데 딱 두 가지 유형의 설정을 만들었다.
이 정보들은 ztunnel 파드에 localhost:15000/config_dump
요청을 날려 확인하거나 istioctl pc workload
명령을 통해서도 확인할 수 있다.
{
"workloads": {
"10.244.2.8": {
"workloadIp": "10.244.2.8",
"protocol": "TCP",
"name": "helloworld-v1-cross-node-55446d46d8-ntdbk",
"namespace": "default",
"serviceAccount": "helloworld",
"workloadName": "helloworld-v1-cross-node",
"workloadType": "deployment",
"canonicalName": "helloworld",
"canonicalRevision": "v1",
"node": "ambient-worker2",
"authorizationPolicies": [],
"status": "Healthy"
}
}
}
이게 기본적인 workloads
설정으로, 해당 노드의 어떤 파드가 메시의 워크로드로 관리될지를 결정한다.
구체적으로는 이 설정 정보들을 istiod가 전달해서 받는 건 아니고, 노드에 띄워진 워크로드의 정보는 ztunnel이 알아서 노드에서 수집하여 구성한다!
그 이후 istiod가 전달하는 설정은 여기에서 protocol 필드를 값을 바꾸는 것뿐이다.
{
"workloads": {
"10.244.2.8": {
"workloadIp": "10.244.2.8",
"protocol": "HBONE",
...
}
istiod는 위와 같이 특정 워크로드의 프로토콜을 HBONE으로 설정하거나, 말거나 하는 정도의 설정만을 전달한다.
이게 HBONE으로 설정되면 해당 워크로드를 메시로 관리하겠다는 뜻이며 ztunnel은 이때부터 모든 통신을 관리하기 시작한다.
엔보이에서 mtls 설정을 하기 위해 전달하는 데이터 양과 비교하면 벽돌폰과 스마트폰에 버금가는 차이가 있다..!
한편 XDS 설정 중 두번째인 policies
는이름 그대로 접근 정책을 설정한다.
{
"policies": {
"default/hw-viewer": {
"name": "hw-viewer",
"namespace": "default",
"scope": "WorkloadSelector",
"action": "Allow",
"groups": [[[{
"principals": [{"Exact": "cluster.local/ns/default/sa/sleep"}]
}]]]
}
}
}
istiod는 이런 식으로 어떤 워크로드를 고르고 조건과 액션을 전달한다.
그럼 해당 설정은 워크로드 정보에 이런 식으로 반영된다.
{
"workloads": {
"10.244.2.8": {
"workloadIp": "10.244.2.8",
...
"authorizationPolicies": [
"default/hw-viewer"
],
}
}
어떤 워크로드에 인가 정책들을 리스트로 표시하고 해당 워크로드로 향하는 트래픽에 정책을 적용시키는 것이다.
웨이포인트(Waypoint)
웨이포인트는 앰비언트 모드에서 L7 관련 기능을 처리하기 위한 프록시이다.
ztunnel은 메시에서 워크로드 별 신원을 인증하고 안전한 통신이라는 최소 요건을 달성시킨다.
(앰비언트를 공부하며 메시의 핵심 기능을 여태 내가 간과한 것 같다는 생각이 들어 계속 강조하고 있다.)
여기에 트래픽 분할, 재시도, 타임아웃이나 헤더 기반 인가 등의 다양한 세부 제어를 하고자 할 때는 기존에 사용하던 엔보이를 그대로 활용한다.
웨이포인트는 항공, 항해 경로 상의 주요 지점을 가리키는 용어로, 비행사가 비행 계획과 경로를 짤 때 활용하는 거점을 가리킬 때 쓰인다.[12]
웨이포인트라는 표현은 앰비언트 모드에서의 엔보이의 기능적 역할과 설정 방식을 잘 나타내는 표현이라고 생각한다.
그냥 프록시라던가, 아니면 게이트웨이라는 표현을 쓰지 않은 이유를 생각하면서 핵심 특징을 정리해봤다.
(웨이포인트의 기능 자체는 그냥 엔보이라 구태여 정리하지 않는다.)
네임스페이스 종속
먼저 웨이포인트는 네임스페이스 단위로 생성되어 해당 네임스페이스에서 관리를 받고 싶은 서비스들에 웨이포인트를 지정하는 방식으로 설정된다.
위 그림 상에서는 S1, S2에 설정을 걸었기에 해당 서비스로 가는 트래픽은 웨이포인트를 경유하게 된다.
이때 메시 내의 트래픽이라면 위에서 말한 HBONE 터널을 통해 암호화돼 전달될 것이다.
아닌 케이스라면 그냥 들어오게 될 것이다.
이렇게 네임스페이스 별로 생성되며 작은 단위의 그룹에 대한 게이트웨이 역할을 수행하기에, 게이트웨이라는 표현을 피한 것으로 보인다.[13]
보통 게이트웨이는 주로 네트워크의 경계에서 남북 트래픽을 처리하는 잘 정의된 진입점의 의미를 가지기 때문이다.
목적지 기반 트래픽 제어
여기에 더불어 엔보이를 사용하는 방식이 출발지 기반에서 목적지 기반으로 바뀐 것이 매우 큰 특징이다.
다음은 웨이포인트의 CDS 설정에 대한 예시이다.
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
agent - - - STATIC
connect_originate - - - ORIGINAL_DST
encap - - - STATIC
kubernetes.default.svc.cluster.local 443 tcp inbound-vip EDS
main_internal - - - STATIC
prometheus_stats - - - STATIC
reviews.default.svc.cluster.local 9080 http inbound-vip EDS
reviews.default.svc.cluster.local 9080 http/v1 inbound-vip EDS
reviews.default.svc.cluster.local 9080 http/v2 inbound-vip EDS
reviews.default.svc.cluster.local 9080 http/v3 inbound-vip EDS
sds-grpc - - - STATIC
xds-grpc - - - STATIC
zipkin - - - STRICT_DNS
기존 엔보이는 15001 포트의 리스너 virtualOutbound에 originDstPort가 true로 설정되어 메시 내 모든 서비스에 대한 정보를 내부 리스너로 처리했다.
그러나 웨이포인트는 자신을 경유하게 하려는 목적지에 대한 정보만을 가지고 있다.
(DIRECTION에 inbound-vip가 핵심)
이제 엔보이는 exportTo
나 이스티오 사이드카 등을 활용해 인식할 대상을 한정짓기 위해 눈물의 최적화 똥꼬쇼를 벌일 필요가 없게 됐다.
정리하자면, 웨이포인트는 네임스페이스 단위로 붙고, 목적지가 되는 서비스 근처에서 잠시 들리는 경유지로서 기능한다.
여기에서 각종 인가 정책이나 트래픽 제어를 수행하게 되는 것이다.
그렇기에 출발지로부터 나가는 트래픽이 잠시 들리는 전초기지가 아니라, 들어오려는 트래픽이 잠시 경유해서 제어를 받게 되는 중간 지점으로서의 웨이포인트라는 이름이 붙은 게 아닐까 싶다.
목적지 기반 제어 방식은 메시 내의 서비스를 대상으로 할 때 충분히 잘 동작한다.
가령 재시도나 타임아웃 등의 설정을 넣는다고 쳐보자.
그렇다면 웨이포인트에서 실제 워크로드로 트래픽을 날릴 때 해당 기능들이 적용되면 된다.
그러나 아쉬운 지점은 목적지에 웨이포인트가 없는 경우, 즉 외부로 나가는 요청에 대해서는 처리할 수 없다는 것이다.[14]
앰비언트 모드는 서비스를 제공하는 측이 트래픽 제어를 설정하는 경우를 전제하고 설계됐기에 사실 이러한 케이스에 대한 완벽한 해결책을 제시하지 못하고 있다.
이 부분에 대해서 커뮤니티에서는 활발한 논의와 개발이 진행되고 있다고 한다.
동작 원리
이제부터 앰비언트 모드가 돌아가는 핵심 플로우와 원리들을 탐구해본다.
여기에서부터 기술적인 내용이 많이 나온다.
공식 블로그에 여러 글들이 있는데, 앰비언트 모드가 발전하면서 많은 부분이 변경되었다.
현재 버전을 기준으로 설명한다.
ztunnel 트래픽 리디렉션
앰비언트에서 모든 트래픽이 기본적으로 거치게 되는 ztunnel.
사이드카 모드에서는 파드 내부에 프록시가 같이 배치된 채 모든 트래픽을 받아 처리할 수 있었다.
그럼 같은 노드에 있을 뿐인 ztunnel은 도대체 어떻게 트래픽을 받고, 보낼 수 있는 것일까?
패킷 리디렉션 경로 생성
먼저 워크로드가 노드에 세팅됐을 때, 그리고 그 워크로드가 앰비언트 모드에 속하도록 설정됐을 때 일어나는 일을 알아보자.[15]
- 파드 식별
- 워크로드가 앰비언트에 속하는 상황은 앰비언트 설정이 된 파드가 생성됐을 때, 기존 파드가 앰비언트 설정이 됐을 때이다.
- 이제 워크로드와 라이프사이클이 다르기 때문에 이렇게 두 상황이 연출될 수 있다!
- 파드가 생성되는 경우
- 기존 파드가 세팅되는 경우
- 이런 경우도 보장하기 위해 istio-cni는 평소에 kube-apiserver와 통신하며 파드의 생성을 감시한다.
- 워크로드가 앰비언트에 속하는 상황은 앰비언트 설정이 된 파드가 생성됐을 때, 기존 파드가 앰비언트 설정이 됐을 때이다.
- 해당 파드의 네임스페이스에 들어가서 iptables를 조작한다.
- 이 시점에 이미 해당 파드의 네임스페이스 파일 디스크립터를 알고 있다는 것.
- 아마 CRI 곁에 붙은 플러그인을 통해 정보를 확인하는 것 같은데, 정확하게는 모르겠다.
- istio-cni는 ztunnel에 UDS(Unix Domain Socket)로 통신 창구가 뚫려 있어 이 통로를 통해 ztunnel 파드에 세팅을 해야 함을 알린다.
- 이때 파드의 네트워크 네임스페이스 파일 디스크립터 정보를 넘긴다.
- ztunnel은 해당 정보를 통해 파드의 네트워크 네임스페이스에 진입하고, 15008, 15006, 15001 포트로 리스닝 소켓을 생성한다.
정리하자면, istio-cni가 파드를 감지하고 내부에 트래픽 경로를 조작하는 역할을 수행한다.
이후에 ztunnel은 조작된 경로에서 트래픽을 송수신할 수 있는, 소위 소켓 빨대(socket straw)를 꼽는 것이다.
이 동작 원리를 명확하게 이해하기 위해 간단하게 리눅스의 네임스페이스가 무엇인지 알면 좋다.
네임스페이스는 프로세스 간 자원을 독립시키는 기술로 서로 다른 네트워크 네임스페이스는 다른 인터페이스, iptables, 소켓 정보를 가진다.
그런데 사실 프로세스도 결국 하나의 파일로 관리되기에 관련한 네임스페이스 정보 역시 파일로 관리되는데, 이걸 사용하면 해당 네임스페이스에 접근할 수 있게 된다.
이걸 파일 디스크립터로 그대로 가져와서 setNs를 하면 되는, 아주 단순한 작업이다.
파드의 네임스페이스에서 만들어진 소켓은 파드의 네임스페이스에 속하게 되나, 결국 ztunnel이 자신의 네임스페이스에서 데이터를 받도록 하는 창구가 되어준다.
다른 네트워크 네임스페이스의 파일 디스크립터만 알면 해당 네임스페이스에 소켓을 열 수 있다는 게 조금 궁금해서 간단하게 직접 코드를 만들어 테스트해봤다.
I-다른 네임스페이스 같은 포트 리스닝 서버 구현 참고.
이러한 방식의 가장 큰 특징은 실상 ztunnel로 향하는 통로가 파드 네임스페이스 내부에 있다는 것이다.
기존의 사이드카의 단점을 덜면서도 ztunnel은 거의 사이드카마냥 가까운 영역에서 트래픽을 처리할 수 있게 된 것이다.
컨테이너 격리의 기본이 되는 기술인 만큼, 사실 네임스페이스에 접근해서 접속하는 것은 막강한 권한을 필요로 한다.
그래서 istio-cni, ztunnel 전부 CAP_SYS_ADMIN을 가지고 실행된다.
iptables 상세
그럼 istio-cni는 구체적으로 어떻게 테이블을 수정하는지도 살펴보자.
istio-cni는 iptables의 raw, mangle, nat 테이블을 수정한다.
이때 raw 테이블을 조작하는 방식은 정확하게 사이드카 모드에서 하던 방식과 같다.
궁금하다면 E-이스티오의 데이터 플레인 트래픽 세팅 원리를 참고하도록 한다.
먼저 mangle 테이블은 이렇게 생겼다.
Chain PREROUTING (policy ACCEPT 675 packets, 158K bytes)
pkts bytes target prot opt in out source destination
675 158K ISTIO_PRERT 0 -- * * 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_PRERT (1 references)
pkts bytes target prot opt in out source destination
48 3749 CONNMARK 0 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x539/0xfff CONNMARK xset 0x111/0xfff
Chain OUTPUT (policy ACCEPT 688 packets, 107K bytes)
pkts bytes target prot opt in out source destination
688 107K ISTIO_OUTPUT 0 -- * * 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
346 75875 CONNMARK 0 -- * * 0.0.0.0/0 0.0.0.0/0 connmark match 0x111/0xfff CONNMARK restore
이 테이블 정보를 이해하기 위한 몇 가지 지식이 있다.
iptables에서 match a / b
의 의미는 패킷 c에 대해 c & b == a
를 묻는 것이다.
즉 AND연산을 하는 것인데, 0xfff(1로 채워진 12비트)에 AND 연산을 한다는 건 그냥 하위 12 비트가 a와 같냐고 묻는 것과 같다.
connmark와 mark가 구제적으로 다르다는 사실에 유의해야 한다.
connmark는 컨트랙 모듈이 관리하는 커넥션 엔트리에 마킹되는 값으로, 커널 내부에서 패킷 구조체에 마킹되는 값과 다르다.
룰 해석 자체는 그렇게 어렵지는 않다.
들어오는 트래픽에 대해서는 마크가 0x539일 경우 커넥션 마크를 0x111로 세팅한다.
그리고 나가는 트래픽의 커넥션 마크에 0x111이 세팅되어 있다면 해당 값을 일반 마크에도 세팅한다.
이 마킹 정보들은 이후 nat 테이블에서 사용된다.
여기에서 0x539는 ztunnel측에서 세팅해주는 값으로 십진수로는 1337이다.
(1337은 이스티오에서 프록시에 부여하는 리눅스 유저 ID 값으로, 프록시를 대표하는 숫자라고 봐도 될 것 같다.)
즉 ztunnel이 처리하는 패킷이라면 무조건 해당 마크가 붙는다.
구체적으로 어떻게 세팅되는 걸까 궁금해서 코드를 조금 들여다보았다.[16]
(러스트는 이전에 취미 삼아 알음알음 문법만 봐서 솔직히 코드를 알아보기 힘들었다.)
ztunnel의 컨피그를 담는 구조체에 패킷 마크 값이 세팅돼있다.
해당 마크는 파드 내부에 배치하는 소켓에 설정된다.[17]
다음으로 nat 테이블은 이런 식으로 구성된다.
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ISTIO_PRERT 0 -- * * 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_PRERT (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT 6 -- * * 169.254.7.127 0.0.0.0/0 tcp
0 0 REDIRECT 6 -- * * 0.0.0.0/0 !127.0.0.1 tcp dpt:!15008 mark match ! 0x539/0xfff redir ports 15006
Chain OUTPUT (policy ACCEPT 1 packets, 60 bytes)
pkts bytes target prot opt in out source destination
49 3756 ISTIO_OUTPUT 0 -- * * 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT 6 -- * * 0.0.0.0/0 169.254.7.127 tcp
24 2256 REDIRECT 17 -- * !lo 0.0.0.0/0 0.0.0.0/0 mark match ! 0x539/0xfff udp dpt:53 redir ports 15053
0 0 REDIRECT 6 -- * * 0.0.0.0/0 !127.0.0.1 tcp dpt:53 mark match ! 0x539/0xfff redir ports 15053
0 0 ACCEPT 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x111/0xfff
0 0 ACCEPT 0 -- * lo 0.0.0.0/0 !127.0.0.1
24 1440 REDIRECT 6 -- * * 0.0.0.0/0 !127.0.0.1 mark match ! 0x539/0xfff redir ports 15001
이전 사이드카 모드 때의 iptables와 비교하면 꽤나 간결한 편이다.
테이블이 두 개 뿐이라 해석 자체는 상대적으로 쉽다.
먼저 들어오는 패킷에 대해 169.254.7.127에서 왔다면 무조건 통과시킨다.
해당 주소는 kubelet에서 오는 헬스체크 프로브에 대해 ztunnel이 SNAT 시키는 주소로, 이를 통해 헬스체크 패킷은 정상적으로 동작하게 만든다.[18]
그리고 다음은 15006으로 리다이렉트를 시키는 조건이 적혀있는데, 간단하게 말하자면 그냥 앱 컨테이너에서 나가는 요청을 낚아채는 것이다.
사이드카 모드와 달리, 이제는 룰에 명시되지 않은 나머지 패킷들이 그냥 통과한다는 것을 유의할 필요가 있다.
나가는 트래픽이 이번에도 조금 복잡하다.
- 1 - 프로브 요청 처리
- 2 - DNS 질의 처리
- ztunnel이 istiod로부터 도메인을 받는 것에 대한 로그는 없어서 이 부분은 구체적으로 모르겠다.
- 다만 앰비언트 환경에서도 서비스 엔트리가 동작한다는 건 DNS 프록시 역할을 해주는 것이 아닐까 한다.
- 3 - 0x111 마킹 여부를 보는데 mangle 테이블에서 마킹된 것들은 그냥 통과를 시켜버린다!
- 0x111 마크는 이미 ztunnel에 의해 처리가 완료된 패킷을 나타내며, 이러한 방식을 통해 한 커넥션의 패킷들은 상대적으로 빠르게 검사를 통과할 수 있다.
- 4 - localhost가 아닌 주소로 자기 자신을 호출하는 트래픽 통과
- 이것만 보면 헷갈리겠지만, 그럼에도 사이드카 모드 때와 마찬가지로 메시 내의 호스트 자신으로 요청을 보내는 트래픽은 메시의 정책을 적용 받는다.
- lo 인터페이스를 타지 않기 때문에 이 룰을 통과하기 때문이다.
- 5 - 아웃바운드 리다이렉트
- 들어오는 패킷과 비슷하게, 그냥 앱 컨테이너에서 밖으로 나가는 트래픽이면 낚아챈다.
- 그 외 - ztunnel이 보내는 트래픽(무조건 0x539가 붙음), HBONE을 타고자 하는 트래픽 등
구체적으로 해당 마킹이 끼치는 영향을 알고 싶어서 여러 실험을 진행해봤으나, 3번 룰에 걸리는 패킷을 찾는 것에 실패했다.
시도해본 것으로는,
- 자기 자신 호출
- 여러 번 호출
- 에코 서버 배치해서 지속 연결
이 정도 해봤는데, 어떤 상황에서도 해당 룰이 카운트되는 것을 보지 못했다.
(시간 나면 http 헤더로 keep alive를 보내는 웹소켓으로 다시 테스트해볼 듯)
사실 컨트랙이 되는 시점에서 한 tcp의 후속 패킷은 nat 테이블을 우회하는 것으로 알고 있는데, 저 룰이 정확하게 적용되는 시점은 무엇일까.
지미 송은 앰비언트 모드에서 패킷의 경로가 최적화된다고 설명한다.[19]
여기에서 말하는 최적화가 iptables에 적힌 것과 관련이 있지는 않을까 했지만, 아직까지는 정말 모르겠다.
컨트랙을 통한 최적화를 말하는 거라면 해당 사항은 비단 앰비언트 모드에만 한정되는 것이 아니다.
다만 HBONE 터널이 오래 컨트랙이 지속되는 것은 확인했는데, 어쩌면 이걸 말하는 것일지도 모르겠다.
ztunnel 내부 코드를 봤을 때 같은 워크로드로의 HBONE 재연결에 대해서는 이전에 사용한 포트를 다시 쓰는 부분이 있다.
아무튼 0x111이 정확하게 언제 사용되는지에 대해서는 아직 내 이해가 미치지 못했다고 결론 내리고 넘기겠다.
해당 부분을 명확하게 이해하고 난 후 트래픽 경로에 iptables 룰을 적용시켜 도식화시키려고 한다.
트래픽 경로
이제 트래픽을 리디렉션하는 원리는 알았고, 실제 트래픽이 어떻게 흐르는지도 살펴보자.
핵심적으로 이해해야 할 것은 ztunnel이 트래픽을 보내는 방식이다.
아웃바운드 트래픽
일단 아웃바운드 트래픽, 그 중에서도 메시 내 워크로드로 향하는 트래픽을 알아보자.
워크로드의 패킷은 iptables에 의해 ztunnel이 수신하는 소켓으로 들어간다.
(ztunnel은 파드의 네임스페이스 내에서 해당 패킷을 받아내기 때문에 살짝 겹쳐서 그렸다.)
이후 ztunnel은 대상 워크로드의 주소로 트래픽을 날리는데, 구체적으로는 HBONE이라는 암호화 터널을 이용한다.
대상 워크로드의 네임스페이스에는 또 15008로 ztunnel이 리스닝을 하고 있으며, 이 통로를 통해 HBONE 프로토콜을 통한 송수신이 이뤄진다.
(참고로 보내는 ztunnel은 그냥 임의의 포트를 이용해서 보낸다. 터널이랍시고 뭐 같은 포트 쓰고 그런 건 아님.)
이 트래픽은 완전히 암호화되기에 파드의 외부에서는 절대 확인할 수 없다.
수신 측 ztunnel은 암호화된 데이터를 복호화하여 해당 요청을 확인하여 그대로 대상 워크로드에 요청을 날린다.
당연히 응답 패킷은 이 역순으로 흐른다.
여기에서 재밌는 지점은 결국 모든 트래픽은 파드의 네트워크 네임스페이스를 통해서만 이뤄진 것처럼 보인다는 것이다.
ztunnel 간의 트래픽 송수신 과정도 ztunnel의 네임스페이스에서 보내는 게 아니기 때문에, 외부에서 보면 그냥 진짜 파드끼리 통신한 것처럼 보이게 된다.
그런데 중간 과정에서 패킷은 암호화되는 ㄷㄷㄷ
아무튼 이러한 방식만 이해한다면 다른 트래픽 케이스들은 그다지 어려울 게 없다.
남은 케이스라고 한다면 메시가 아닌 곳으로 트래픽이 가는 경우와, L7 기능이 적용된 웨이포인트를 경유해야 하는 경우가 있다.
메시 외부로 가는 트래픽이라면 ztunnel은 그냥 그대로 트래픽을 보낸다.
웨이포인트로 갈 경우에도 HBONE을 이용하게 되며, 웨이포인트 역시 HBONE을 위한 15008 포트를 리스닝하고 있다.
그래서 웨이포인트를 거치는 트래픽의 흐름은 그저 한 홉이 늘어나는 것에 불과하다.
참고로 ztunnel은 노드마다 위치하기 때문에 같은 노드에서는 혹시 뭔가 다른 게 있을까 궁금할 수도 있겠다.
지미 송의 글을 보면 같은 노드에서는 별도의 암복호화 과정이 드러나지 않고 바로 대상 워크로드에 전달되는 것으로 전달되는 것처럼 보이지만...[20]
직접 실험해본 결과는 전혀 달랐다.
같은 노드에서 통신을 한다고 해도 그냥 다른 노드에 있는 것과 다를 것 없이 ztunnel은 15008 포트를 이용해 HBONE 통신을 진행한다.
인바운드 트래픽
위 방식을 이해했다면 인바운드 트래픽도 쉽게 이해할 수 있을 것이다.
먼저 메시에서 들어오는 트래픽의 경우에는 어차피 ztunnel이나 웨이포인트를 통해서 들어오게 되며, 이들은 HBONE 프로토콜을 사용하므로 15008포트로 들어오게 된다.
수신 측 ztunnel이 해당 트래픽을 잘 복호화해서 워크로드에게 다시 요청을 날릴 것이다.
메시 외부에서, 평문 트래픽이 날아오는 경우에는 iptables를 통해 ztunnel의 인바운드 핸들링 포트인 15006으로 리다이렉트된다.
이러나 저러나 결국 ztunnel을 경유하게 되는 것은 똑같다.
메시 내부에서 웨이포인트 경유하는 트래픽
추가적으로 재밌게 볼 만한 트래픽 경로가 있다.
ztunnel은 모든 노드에 배치되고, 같은 노드 내의 워크로드 통신에서도 트래픽을 가로채 암호화를 적용한다.
그래도 단일 노드 내에서 트래픽이 오가며 외부로 나갈 일은 당연히 없다.
그러나 여기에 L7 기능이 필요해 웨이포인트를 경유하도록 하면 상황이 조금 달라질 수 있다.
웨이포인트는 노드마다 배치되지 않고 네임스페이스 종속적이기 때문에, 웨이포인트가 노드 외부에 있다면 트래픽 경로가 조금 늘어날 수 있다.
바로 이런 식으로 말이다.
기존에는 A와 B의 통신이 노드 내에서 끝났지만 다른 노드에 있는 웨이포인트를 경유하기 위해서 부득불 외부로 트래픽이 나가는 상황이 연출될 수 있다.
물론 HBONE은 통신을 암호화해주기 때문에 보안적 문제는 없다.
의문이 생기는 지점은 아무래도 노드 간 홉이 증가함으로 인해 발생하는 레이턴시일 것이다.
같은 노드 내 워크로드 간 통신은 노드 내에서 끝나는 것이 보장됐던 사이드카 모드와 대조되는 지점이다.
노드와 관련이 없는 웨이포인트의 등장으로 인해 최악의 상황에는 같은 지역에서 끝났어야 할 트래픽이 지역을 넘어가는 불상사가 발생할 수 있다는 것을 인지할 필요가 있다.
이에 대해 이스티오 측에서는 실질적인 네트워크 레이턴시 이슈는 L7 데이터 처리에서 발생한다는 점을 든다.[21]
현대 클라우드 환경의 네트워크 자원은 충분하며 정말 병목이 생기는 지점은 따로 있다는 것이다.
앰비언트의 아키텍처에서는 L7을 담당하는 웨이포인트는 얼마든지 스케일링이 가능하고 경유 대상이 될 워크로드를 커스텀할 수 있기 때문에 오히려 네트워크 지연에 있어 효과적인 성능 향상을 이룰 수 있다는 것이 이스티오 커뮤니티의 주장이다.
HBONE(Http Based Overlay NEtwork)
그렇다면 여태까지 계속 이야기한 HBONE이 무엇인지도 알아보자.
HTTP Based Overlay Network는 말 그대로 HTTP를 기반으로 한 오버레이 네트워크를 구성하는 프로토콜이다.
앰비언트 모드가 구상되며 이스티오 커뮤니티에서 해당 프로토콜을 설계했고, 최종적으로 빠르고 안전한 패킷 암호화 터널이 완성됐다.
구체적으로 L3에서 HBONE의 패킷은 이런 모양을 가지고 있다.[22]
이 프로토콜은 무려 3가지 표준 기술을 통합한 콤비네이션이다.[23]
- TLS - 모든 통신은 ztunnel, waypoint의 신원을 기반으로 암호화된다.
- CONNECT - 터널 연결을 유지하는 메서드로 활용된다.
- HTTP/2 - 터널 위에 트래픽을 멀티 플렉싱한다.
요약하면 그저 HTTP의 CONNECT 메서드를 이용해 스트림을 유지해두고 그 위로 워크로드의 트래픽을 그대로 실어 나르는 것일 뿐이다.
어쩌면 간단한 아이디어이지만, 이 방식의 가장 큰 특징은 트래픽의 투명성이다.
ztunnel 간 통신은 HBONE을 통해 이뤄지나, 실제로 통신을 하게 되는 두 워크로드 간의 패킷은 그대로 보존되어ㅑ 워크로드들은 조작되지 않은 (것 마냥) 통신이 가능하다.
HTTP 헤더에 집중하여 패킷을 바라본다면 이런 모양이 된다.[24]
TLS 핸드쉐이크 이후 암호화 통신이 수립되면 그 이후 CONNECT 연결을 수행한다.[25]
이때 ztunnel은 기존 워크로드의 신원과 트래픽이 가야할 경로 등을 전달한다.
구체적으로 들어가는 헤더는 다음의 것들이 있다.[26]
:authority
- 원본 타겟X-Forwarded-For
- 원본 출발지baggage
- 트레이스를 위한 헤디엍.
HTTP/2 기반으로 한 커넥션에서 멀티 플렉싱 스트림을 구성한다는 것 이외에 추가적으로 주목할 만한 점은 연결 수립의 유지.
상대적으로 HBONE 연결에 대해서 컨트랙을 길게 가져가서 재요청, 중복 경로가 발생할 때 정말 TCP 터널로서 동작한다.
(이 부분은 확실하지 않은데, 코드를 보면 출발지 ztunnel측에서 사용된 임의의 포트를 재사용하는 것 같다.)
앰비언트 모드의 주된 성능 최적화 원리 중 하나라 하겠다.
여기 내용을 보면 스피페 기반의 워크로드 신원을 사용한다고 하는데, 패킷을 뜯어본 바로는 암호화 터널 수립 간에 이걸 사용하는지는 잘 모르겠다.[27]
근데 프로토콜 구조 상으로는 워크로드의 신원이 암호화 수립 간에 사용되는 게 적합해보이고, 또 ztunnel 로그를 봤을 때도 그러한 방식이 맞는 것으로 보인다.
나중에 추가적으로 확인해봐야 할 것 같다.
웨이포인트 설정
핵심적인 동작 원리 중 마지막으로 볼 것은 웨이포인트가 설정되는 방식이다.
여태 기존 사이드카 모드에서의 엔보이가 설정되는 방식은 출발지 기반으로 설정된다고 이야기했다.
그러나 웨이포인트의 엔보이는 완전히 설정 방식이 달라진다.
웨이포인트 엔보이의 내부 설정은 이런 식으로 구성돼있다.
먼저 HBONE으로 들어온 데이터를 필터를 거쳐 복호화 하면서 가상의 클러스터로 전달한다.
이 클러스터는 사실 내부의 main_internal 리스너이며, 해당 리스너에 목적지가 될 여러 서비스나 워크로드를 매칭하는 개별 리스너가 위치한다.
그래서 각각 조건에 매칭되는 트래픽이 설정을 넣은 각종 L7 필터를 거치고 최종적으로 대상에 15008 포트로 요청을 전달한다.
그럼 해당 ztunnel이 트래픽을 처리하게 될 것이다.
이때 주의할 것은 웨이포인트에서 대상으로 보낼 때 사용되는 신원은 웨이포인트의 신원이라는 것이다.
즉 메시 내부에서 A에서 B로 가는 요청일 때 실제로 확인해보면 A에서의 요청은 웨이포인트의 신원을 확인하게 된다.
반대로 웨이포인트에서 B로 가는 요청 역시 웨이포인트의 신원이 사용된다.
만약 L4 레벨에서 인가 정책을 설정하고자 한다면 웨이포인트의 신원이 사용되는 지점을 명확하게 인지하여 정책을 설정해야 한다.
메시 외부에서 웨이포인트를 경유하는 트래픽
여기에서 위에서 다루지 않은 트래픽 흐름을 하나 짚어보자.
기본적으로 웨이포인트는 HBONE 프로토콜 트래픽을 받아서 처리하는데 이러한 요청을 보낼 수 있는 것은 여태 봤을 때 ztunnel이었다.
그렇다면 외부에서 날아오는 요청은 웨이포인트를 거칠 수 있는가?
결론만 말하자면, 메시 외부의 트래픽은 웨이포인트를 거칠 수 없다.
이러한 트래픽이 발생하는 케이스를 세부적으로 나눠보겠다.
진짜 외부임;
사실 이건 웨이포인트를 거치지 못하는 게 당연하다.
기존 사이드카 모드에서도 이런 트래픽이 들어오는 것 자체는 해결할 수 없다.
운영 조직은 이런 트래픽을 허용하지 않도록 L4의 인가 정책을 엄격하게 제한할 필요가 있다.
클러스터 안에 있긴 한데 메시는 아님
서비스 메시를 적용하지 않은 네임스페이스에 있는 파드가 요청을 날리는 경우는 어떠한가?
해당 파드는 ztunnel의 관리를 받지 않는 관계로, 트래픽을 정직하게 대상 워크로드로 바로 날아간다.
그럼 해당 워크로드로 트래픽이 들어가 결국 15006 포트를 통해 ztunnel이 받아서 처리해줄 것이다.
즉, 이러한 트래픽 역시 웨이포인트를 경유할 수 없다.
언뜻 보면 웨이포인트를 경유해야 하는 게 아닌가 싶지만 메시의 관점에서는 해당 트래픽 역시 외부인 것이나 다름 없다.
외부인데 게이트웨이를 거침
마지막으로, 잘 뚫어둔 게이트웨이를 거쳐서 들어오는 트래픽이라면 어떠한가?
트래픽을 받아 처리할 게이트웨이는 틀림없이 메시의 일원이다.
하지만 이 트래픽도 웨이포인트를 거치지 않는다![28]
게이트웨이 엔보이에 대해 ztunnel이 트래픽을 조작하진 않기 때문이다.
그리고 이 게이트웨이는.. 엔보이임에도 웨이포인트가 해줬어야 할 각종 L7 기능을 처리하지 않는다!
향후 앰비언트 모드가 업그레이드되며 메시 내의 모든 컴포넌트가 웨이포인트를 인지할 수 있도록 개선될 예정이라고 한다.
(첨언하자면, 같은 메시로 운영되는 사이드카 모드로부터의 트래픽도 웨이포인트를 거치지 않는다.)
사이드카 모드와의 비교
앰비언트 모드가 어떤 식으로 이뤄져있고, 어떻게 동작하는지 충분히 설명이 된 것 같다.
마지막으로 그래서 기존 모드와 어떻게 다른지를 조명 해보고자 한다.
성능
가장 중요한 성능적인 차이에 대해서 짚어보자면, 일단 앰비언트 모드는 모든 영역에서 확실하게 사이드카 모드를 압도한다.[29]
네트워크 성능
일단 네트워킹 속도를 봤을 때는 근소한 정도의 차이를 보인다.
최소한 앰비언트 모드로 인해 네트워크 성능이 하향되는 걱정은 할 필요 없다.
여기에 더불어 트래픽을 암호화하는 솔루션 사이에서 TCP 처리량 비교글도 있다.[4:1]
해당 테스트는 Iperf를 이용해 진행됐는데, 보다시피 앰비언트는 독보적인 성능을 자랑한다.
x86 16코어 환경이었다는데, 노드 간 연결 인터페이스가 뭐였길래 28기가가 나오는지..는 정말 궁금하다.
(eBPF를 사용해서 트래픽을 최적화한다는 cillium이 저렇게나 압도적으로 낮은 성능을 보인다는 게 놀랍다.)
cpu도 근소한 차이를 보이는데, 메모리의 경우에는 앰비언트 모드가 확실하게 우월하다.
기본적으로 배치되는 컴포넌트가 적고, ztunnel 자체가 메모리 효율적이기 때문에 그런 것으로 보인다.
추가적으로, 오히려 메시가 세팅되지 않은 환경보다 앰비언트 모드가 더 빠른 케이스가 존재한다.[30]
(여담으로 이 분석글 정말 대단한 거 같다 갓 린 선.. )
결론적으로만 보자면 ztunnel이 HBONE 통신 과정에서 HTTP/2의 멀티플렉싱을 적극 활용하여 트래픽을 최적화하는데, 이게 다량의 트래픽을 최적화하는 효과가 너무 뛰어나서 패킷의 홉이 늘어나는 것을 상쇄하기도 한다는 것.
흔하게 발생할 수 있는 케이스는 아니지만 그럼에도 충분히 놀라운 결과라고 생각한다.
컨트롤 플레인 성능
워크로드의 변화, 이스티오 리소스 설정 변경 등의 요소들에 대해 컨트롤 플레인이 데이터 플레인을 동기화하는 속도도 성능에서 중요한 부분 중 하나이다.
E-이스티오 컨트롤 플레인 성능 최적화에서도 다뤘지만 메시의 규모가 커질수록 컨트롤 플레인의 부하는 늘어나고 이에 대한 다양한 대응 전략이 요구된다.
검색을 해본 바로는 그다지 앰비언트 모드의 설정 적용 속도에 대한 성능 측정을 하는 글을 찾지 못했다.
이스티오에서 관리하는 테스트 디렉토리에도 동기화 성능 테스트 부분은 없더라.[31]
다만 ztunnel의 개수는 노드의 개수를 따라가고 XDS 설정 양식이 정말 극단적으로 작기 때문에 해당 성능은 사이드카 모드보다는 무조건 빠를 것이라고 추측된다.
이건 언젠가 자세히 테스트를 해봐야겠다.
보안
기존 사이드카 모드에서는 모든 워크로드 파드 내부에서 워크로드의 신원이 관리됐다.
그러나 앰비언트 모드에서는 ztunnel이 모든 워크로드 신원을 가지고 있으며, 원본 패킷에 대한 조작 없이 통신을 수립한다.
최소한 워크로드가 탈취되는 상황에 대해서 앰비언트 모드는 안전하다고 할 수 있다.
그러나 또 한 가지 걱정이 될 수 있는 지점은 ztunnel의 막강한 권한이다.
네임스페이스를 자유롭게 넘나들 수 있도록 ztunnel은 SYS_ADMIN의 권한이 필요하다.
사이드카 모드에 비해 ztunnel의 더 많이 아픈 손가락이 될 수 있다는 것은 인정할 수밖에 없을 것이다.
그렇지만 최소한 어플리케이션과는 엄연히 분리된 환경에 배치되어 있다는 점에 더불어 배치되는 개수가 적다는 점은 ztunnel이 관리 포인트와 공격 표면을 줄인다는 말이기도 하다.[32]
보안에 대해서는 단순성이 더 좋은 전략이라는 관점에서 앰비언트 모드는 위협이 적은 편이라고 할 수 있다.
추가적으로 이스티오에서는 다른 암호화 기능을 제공하는 CNI들과도 비교한다.[33]
ztunnel은 자신이 배치된 노드에 대해서만 접근할 권한을 가지고 있으며 이는 노드 간 암호화를 위해 다른 노드의 정보까지 식별해야 하는 다른 CNI들과 비교되는 사항이다.
웨이포인트는 어떠한가?
위에서 봤듯 웨이포인트는 자신의 신원을 가지고 있는 메시 내에 드러난 일원이다.
그리고 웨이포인트는 자신의 서비스 어카운트를 가지고 있는데 이는 웨이포인트가 손상될 시 웨이포인트만의 문제로 끝난다는 것을 뜻한다.
최소한 메시 내의 신원이 추가적으로 탈취당하는 일은 없다.
확장성
마지막으로 확장성은 몇 가지 지점을 나눠서 보겠다.
- 이스티오 도입, 변경, 버전 업그레이드 등의 메시 운영
- 앰비언트 모드의 모든 컴포넌트는 워크로드의 라이프사이클을 일절 침해하지 않는다.
- 모든 설정은 별도의 재기동 없이 설정될 수 있으며, 이는 메시를 도입할 때, 퇴출시킬 때 전부 해당한다.
- 다만 메시 자체의 버전을 업그레이드할 때 앰비언트 모드는 카나리 배포를 지원하지 않는다.[34]
- 강제적인 블루 그린 배포가 요구된다는 점에서는 조금 아쉬울 수 있겠다.
- 멀티 클러스터, 가상 머신
- 앰비언트 모드는 1.26 버전 기준 이 기능을 아직 지원하지 않는다.
- WASM, 엔보이 필터 등의 프록시 기능 확장
- 이 부분에 대해서도 1.26 버전 기준 아직 앰비언트는 GA를 달성하지 못했다.
앰비언트 모드는 아직까지 활발하게 개발이 진행되고 있으며, 기존 사이드카 모드의 모든 기능을 대체할 수는 없다.[35]
(특히 버츄얼 서비스가 알파인 건 이스티오를 운영하는 기존 조직들에 강제로 GatewayAPI로 마이그레이션을 요구하는 것과 같다고 본다.)
결론
이러한 점에서 보았을 때, 결국 사이드카 모드는 앰비언트 모드가 대체할 수 없는 기능을 보유하고 있다고 볼 수 있다.
사이드카는 안전합니다 여러분
애초에 사이드카 모드를 그럼에도 일등 시민(first-class) 취급하고 있기에 앰비언트 모드가 나왔다고 해서 무조건 앰비언트로 갈아타야 하는 것 아닌가 하는 고민을 할 필요는 없다고 결론을 내릴 수 있겠다.[36]
물론 성능과 보안의 측면이 더 중요한 가치로 고려된다면 적극적인 고려를 해보는 것이 좋다.
한 가지, 앰비언트 모드와 사이드카 모드는 같은 메시에서 공존할 수 있게 개발됐다.
두 모드를 운영하는 것이 더 어려운 운영 난이도를 야기할 수도 있겠으나 최소한 도입할 조직 입장에서는 점진적인 마이그레이션 경로가 제공된다 할 수 있겠다.
하위 문서
이름 | is-folder | index | noteType | created |
---|---|---|---|---|
Istio Traffic Management | true | 0 | knowledge | 2025-04-21 |
Istio Observability | true | 0 | knowledge | 2025-04-28 |
Istio Security | true | 0 | knowledge | 2025-05-04 |
Istio Extenisibility | true | 0 | knowledge | 2025-05-26 |
pilot-agent | false | 1 | knowledge | 2025-04-28 |
앰비언트 모드 | false | 3 | knowledge | 2025-06-02 |
Istio Operator | false | 5 | knowledge | 2025-05-09 |
istioctl | false | 6 | knowledge | 2025-05-12 |
istiod | false | 7 | knowledge | 2025-05-18 |
사이드카 모드 | false | 8 | knowledge | 2025-05-18 |
관련 문서
EXPLAIN - 파생 문서
이름3 | related | 생성 일자 |
---|---|---|
앰비언트 모드에서 메시 기능 활용 | 앰비언트 모드 | 2025-06-07 20:56 |
앰비언트 모드 헬름 세팅 | 앰비언트 모드 | 2025-06-03 19:27 |
앰비언트 ztunnel 트래픽 경로 분석 | 앰비언트 모드 | 2025-06-07 20:36 |
기타 문서
Z0-연관 knowledge, Z1-트러블슈팅 Z2-디자인,설계, Z3-임시, Z5-프로젝트,아카이브, Z8,9-미분류,미완이름4 | 코드 | 타입 | 생성 일자 |
---|---|---|---|
I-ztunnel이 다른 네임스페이스에서 요청 보내는 코드 분석 | Z1 | topic/idea | 2025-06-07 20:44 |
I-다른 네임스페이스 같은 포트 리스닝 서버 구현 | Z1 | topic/idea | 2025-06-07 19:39 |
9W - 앰비언트 모드 구조, 원리 | Z8 | published | 2025-06-07 19:17 |
9W - 앰비언트 헬름 설치, 각종 리소스 실습 | Z8 | published | 2025-06-07 19:27 |
참고
소켓 관련은 여기 참고
http://jkkang.net/unix/netprg/chap4/net4_intro.html
https://istio.io/latest/blog/2022/introducing-ambient-mesh/ ↩︎
https://istio.io/latest/blog/2022/introducing-ambient-mesh/#istio-and-sidecars ↩︎
https://istio.io/latest/blog/2025/ambient-performance/ ↩︎ ↩︎
https://istio.io/latest/blog/2023/waypoint-proxy-made-simple/#shift-source-proxy-configuration-to-destination-proxy ↩︎
https://istio.io/latest/blog/2022/introducing-ambient-mesh/#why-no-l7-processing-on-the-local-node ↩︎
https://istio.io/latest/blog/2023/waypoint-proxy-made-simple/#shift-source-proxy-configuration-to-destination-proxy ↩︎
https://istio.io/latest/blog/2023/rust-based-ztunnel/#a-rust-based-ztunnel ↩︎
https://docs.google.com/document/d/1c2123cKuYsBDpIon9FFdctWTUIMFweSjgwG7r8l3818/edit?tab=t.0 ↩︎
https://github.com/istio/ztunnel/blob/master/ARCHITECTURE.md ↩︎
https://blog.christianposta.com/challenges-of-adopting-service-mesh-in-enterprise-organizations/ ↩︎
https://istio.io/latest/blog/2023/waypoint-proxy-made-simple/#what-if-my-destination-doesnt-have-a-waypoint-proxy ↩︎
https://istio.io/latest/blog/2024/inpod-traffic-redirection-ambient/#istio-ambient-traffic-redirection-the-new-model ↩︎
https://github.com/istio/ztunnel/blob/master/src/config.rs#L91 ↩︎
https://github.com/istio/ztunnel/blob/master/src/inpod/config.rs#L85 ↩︎
https://istio.io/latest/docs/ambient/usage/networkpolicy/#ambient-health-probes-and-kubernetes-networkpolicy ↩︎
https://jimmysong.io/en/blog/istio-ambient-packet-lifecycle-optimization/ ↩︎
https://jimmysong.io/en/blog/beyond-sidecar/#catch-all-traffic-preventing-traffic-escape ↩︎
https://istio.io/latest/blog/2022/introducing-ambient-mesh/#but-what-about-those-extra-hops ↩︎
https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.6 ↩︎
https://docs.google.com/document/d/1Ofqtxqzk-c_wn0EgAXjaJXDHB9KhDuLe-W3YGG67Y8g/edit?tab=t.0 ↩︎
https://github.com/istio/istio/blob/master/architecture/ambient/ztunnel.md#hbone ↩︎
https://istio.io/latest/docs/ambient/architecture/data-plane/#inbound ↩︎
https://livewyer.io/blog/service-meshes-decoded-is-istio-ambient-worth-it/ ↩︎
https://thenewstack.io/ambient-mesh-can-sidecar-less-istio-make-applications-faster/ ↩︎
https://github.com/istio/tools/blob/release-1.26/ambient-tests/README.md ↩︎
https://istio.io/latest/blog/2022/introducing-ambient-mesh/#what-about-security ↩︎
https://istio.io/latest/docs/releases/feature-stages/#ambient-mode ↩︎
https://istio.io/latest/blog/2022/ambient-security/#sidecars-are-still-a-first-class-supported-deployment ↩︎